<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Flirt - a Flash&#153; Runtime</title>
    <link rel="stylesheet" href="style.css" type="text/css" media="screen" />
    <meta name="description" content="Flirt is an open source, extensible Flash&#153; runtime" />
    <meta name="keywords" CONTENT="Flirt, Flash, runtime, player, open source, GPL">
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  </head>

<body>

<div class="title">
Flirt
</div>

<div class="subtitle">
an open source extensible Flash&#153; runtime
</div>

<div class="hr"></div>

<div class="sidebar">

	<div class="sidebarbox">
	<a class="sidebarlink" href="index.html">About</a><br>
	<a class="sidebarlink" href="render.html">Rendering</a><br>
	<a class="sidebarlink" href="script.html">Actionscript</a><br>
	<a class="sidebarlink" href="example.html">Example</a><br>
	Extension API<br>
	<a class="sidebarlink" href="osx.html">OS X</a><br>
	<a class="sidebarlink" href="bugs.html">Bugs</a><br>
	<a class="sidebarlink" href="future.html">Future Directions</a><br>
	</div>

<br>

	<div class="sidebarbox">
	<a class="sidebarlink" href="http://prdownloads.sourceforge.net/flirt/flirt-20040823.tgz?download">Download source</a><br>
	</div>

<br>

	<div class="sidebarbox">
	<a class="sidebarlink" href="mailto:dave@opaque.net?Subject=Flirt">Contact</a><br>
	<br>
	<a class="sidebarlink" href="http://lists.sourceforge.net/lists/listinfo/flirt-devel">Mailing list</a><br>
	<br>
	<a class="sidebarlink" href="http://sourceforge.net/projects/flirt">SourceForge project page</a><br>
	<br>
	<A href="http://sourceforge.net">
	<IMG src="http://sourceforge.net/sflogo.php?group_id=18365&amp;type=1" width="88" height="31" border="0" alt="SourceForge Logo">
	</A>
	</div>

</div>

<div class="content">

<h3>Extension API</h3>

<p>I got this working last night at 3.30am, with a more than a few empty PBR cans on the desk. (I wonder if I could get some kind of sponsorship deal?) It seems to work. It will probably change in the future.</p>

<p>Adding a new class to the ActionScript environment only requires two functions; one to create the class:</p>

<div class="code"><pre>
ddActionClass*
ddPlayer_addClass
(
    ddPlayer* player,
    ddActionClass* superclass,
    const char* name,
    ddNativeFunction constructor,
    int nargs
);
</pre></div>

<p>and another to assign methods to it:</p>

<div class="code"><pre>
void
ddActionClass_addNativeMethod
(
    ddActionClass* classObject,
    char* name,
    ddNativeFunction function,
    int nargs
);
</pre></div>

<p>Here's a very short function which creates a new "Image" class with the <code>render</code>, <code>setWidth</code>, and <code>setHeight</code> methods:

<div class="code"><pre>
void
setupImageClass(ddPlayer* player)
{
  ddActionClass* class =
    ddPlayer_addClass(player, ddActionObjectClass, "Image", Image_constructor, 1);
	
  ddActionClass_addNativeMethod(class, "render", Image_render, 1);
  ddActionClass_addNativeMethod(class, "setWidth", Image_setWidth, 1);
  ddActionClass_addNativeMethod(class, "setHeight", Image_setHeight, 1);
}
</pre></div>

<p>We still have to implement these methods, though, so we'll have to create some functions of the type Flirt likes. A <code>ddNativeFunction</code> is defined here:</p>

<div class="code"><pre>
typedef ddActionValue
  (*ddNativeFunction)(ddActionObject* object, ddActionContext* context, int nargs);
</pre></div>

<p>That is, it takes as arguments: an object, an actionscript context, and the number of arguments the function wants (<code>FUNCTION_VARARGS</code> if it can vary), and returns a <code>ddActionValue</code>. Chances are, we'll want to create a custom data type. We could use normal object properties to store all of our object state, but that's not very flexible; instead, we define a struct that extends the <code>ddActionObject</code> struct so that it can be safely passed around as a <code>ddActionObject*</code>, but carries along our custom data:</p>

<div class="code"><pre>
struct imageObject
{
	ddActionObject parent;
	ddActionMovieClip* clip;
	int width;
	int height;
};

typedef struct imageObject ImageObject;
</pre></div>

<p>Normal method implementations are pretty straightforward compared to the constructor method, so let's look at the more complex code. Remember that our functions have to be <code>ddNativeFunction</code> types, like this:</p>

<div class="code"><pre>
ddActionValue
Image_constructor(ddActionObject* unused, ddActionContext* context, int nargs)
{
</pre></div>

<p>We want to pass a <code>MovieClip</code> object to our constructor, which is why we specified 1 for the <code>nargs</code> argument in <code>ddPlayer_addClass()</code>&mdash;that's the movie clip that we'll render and save with our <code>render()</code> method. Here we pull the passed argument off the script context stack, pull the object out of it (if it exists), and see if it's actually a MovieClip object:</p>

<div class="code"><pre>
  ImageObject* image;
  ddActionValue value = ddActionContext_popValue(context);
  ddActionObject* object = ddActionValue_getObjectValue(value);

  if ( !ddActionObject_isKindOfClass(object, ddActionMovieClipClass) )
    return ddNullValue;
</pre></div>

<p>If that worked, we create an object of our custom type and initialize it as an object:</p>

<div class="code"><pre>
  image = malloc(sizeof(struct imageObject));

  ddActionObject_init((ddActionObject*)image);
</pre></div>

<p>The previous function has marked our object as an Object type, so we have to override that with our custom Image class, otherwise this object will only act like an Object and not an Image. Currently, here's how you do that:</p>

<div class="code"><pre>
  ddActionObject_setClass((ddActionObject*)image, 
    (ddActionClass*)ddActionObject_getPrototype(unused));
</pre></div>

<p>But that's pretty ugly. The &quot;unused&quot; object passed in (that's normally the <code>this</code> object in a function, but there is no <code>this</code> in a constructor) holds a reference to the called class in its prototype. I could pass the class in as that object, but that conflicts with some weird subclassing issues. This will doubtless change soon.</p>

<p>Now we set our custom object attributes. First, we keep a reference to the MovieClip object that was passed in:</p>

<div class="code"><pre>
  image->clip = (ddActionMovieClip*)ddActionObject_retain(object));
</pre></div>

<p>We have to call the <code>ddActionObject_retain()</code> function on the object so that the script environment won't delete the object out from under us if all other references to the object expire.</p>

<p>Finally, we set the rest of our default values and return the new Image object:</p>

<div class="code"><pre>
  image->width = 0;
  image->height = 0;

  return dd_newActionValue_object((ddActionObject*)image);
}
</pre></div>

<p>One last thing&nbsp;remember how we retained that movie clip object? Shouldn't we un-retain it when we're done? Well, yeah, but I haven't added the API for that yet. If you dig into the source, you'll see that classes have hooks for custom destructors as well as overriding the normal object properties set and get functions (for example, we could do <code>image.width = 300</code> instead of <code>image.setWidth(300)</code>). If we were doing this properly, we'd include a destructor that released the movie clip when the Image object itself expired. For now, we'll let it leak..</p>

<h3>Using your extension</h3>

<p>The Flirt source tree contains the full Image object implementation in <code>imageext.c</code>, and a demo program <code>exttest.c</code> shows how it's used. The Makefile knows about it, so you can just do <code>make exttest</code> in the root of the source tree. The OS X project also includes the Image class in its runtime. The Flash movie <code>exttest.swf</code> in the source package demonstrates usage of the Image class; it has a bunch of squiggles in the root clip, and this simple actionscript on frame one of the root timeline:</p>

<div class="code"><pre>
image = new Image(_root);

if ( image.render('test.png') )
  trace('rendered');
else
  trace('failed');
</pre></div>

<p>Run <code>exttest exttest.swf</code>, and if all works properly it renders the contents of the <code>_root</code> clip into the file test.png. Just like the previous example does, but this is done via script code. And that's good.</p>

<div class="hr"></div>

    <div class="footnote">
    <b>All content copyright (C) 2004 Dave Hayden except where noted otherwise.</b>
    <br>
    <b>Macromedia and Flash are registered trademarks of Macromedia, Inc. in the United States and/or other countries.</b>
    </div>

<br>

</div>

</body>
</html>
